package br.com.felipeab.dexter.faces;

import java.io.Serializable;

import javax.faces.event.ActionEvent;

import br.com.felipeab.dexter.bo.PersistenceService;
import br.com.felipeab.dexter.expection.PersistenceException;
import br.com.felipeab.dexter.expection.TransactionException;
import br.com.felipeab.dexter.util.BundleHelper;

/**
 * Classe CrudController
 * 
 * @author Felipe A. Braga <br>
 *         15/06/2012 01:55:51<br>
 * 
 * @param <T>
 */
@SuppressWarnings("unchecked")
public abstract class CrudController<T extends Serializable> extends BasicController<T> implements Serializable {

	private static final long serialVersionUID = -3680754599006189170L;
	private State state;

	/**
	 * Constutor Padrão
	 */
	public CrudController() {
		this.setState(State.DEFAULT);
	}

	/**
	 * Configura o objeto de persistência genêrico
	 */
	protected abstract PersistenceService getPersistenceService();

	/**
	 * Obtém objeto de dominio
	 * 
	 * @return o objeto de dominio
	 */
	public abstract T getDomain();

	/**
	 * Atribui o objeto de dominio
	 * 
	 * @param domain
	 */
	public abstract void setDomain(T domain);

	/**
	 * Controle o evento do botão novo
	 * 
	 * @param event
	 */
	public void newAction(ActionEvent event) {
		createNewDomain();
		setState(State.INSERT);
		newDomain();
	}

	/**
	 * Controla o evento do botão salvar
	 * 
	 * @param event
	 */
	public void saveAction(ActionEvent event) {
		try {
			validate();
			beforeSave();
			save();
			afterSave();
			msgInfo(BundleHelper.getMessage("msg.info.sucess.saveRecord"));
			setState(State.DEFAULT);
		} catch (TransactionException e) {
			msgError(BundleHelper.getMessage("msg.error.savingRecord"));
		}
	}

	/**
	 * Controla o evento do botão editar
	 * 
	 * @param event
	 */
	public void editAction(ActionEvent event) {
		// TODO não finalizado
		setState(State.EDIT);
		validate();
		beforeEdit();
		edit();
		afterEdit();
	}

	/**
	 * Controla o evento do botão excluir
	 * 
	 * @param event
	 */
	public void deleteAction(ActionEvent event) {
		try {
			delete();
			msgInfo(BundleHelper.getMessage("msg.info.sucess.deleteRecord"));
		} catch (TransactionException e) {
			msgError(BundleHelper.getMessage("msg.error.deletingRecord"));
		}
	}

	/**
	 * Controla o evento do botão pesquisar
	 * 
	 * @param event
	 */
	public void searchAction(ActionEvent event) {
		try {
			if (getState() != State.SEARCH) {
				setState(State.SEARCH);
				createNewDomain();
				return;
			}
			beforeSearch();
			search();
			afterSearch();
			if (getList().isEmpty()) {
				msgInfo(BundleHelper.getMessage("msg.info.recordsFound"));
			}
		} catch (TransactionException e) {
			e.printStackTrace();
			msgError(BundleHelper.getMessage("msg.error.searchingRecord"));
		}
	}

	/**
	 * Controla o evento do botão cancelar
	 * 
	 * @param event
	 */
	public void cancelAction(ActionEvent event) {
		setState(State.DEFAULT);
		cancel();
	}

	/**
	 * Controla o evento de selecionar um item na tabela
	 * 
	 * @param event
	 */
	public void rowSelection() {
		setState(State.PREVIEW);
	}

	/**
	 * Cria uma nova instância do dominio
	 */
	private void createNewDomain() {
		if (getDomain() == null) {
			return;
		}
		try {
			this.setDomain((T) getDomain().getClass().newInstance());
			this.clearFields();
		} catch (Exception e) {
			msgError(BundleHelper.getMessage("msg.error.createNewInstance"));
		}
	}

	/**
	 * Novo dominio
	 */
	protected void newDomain() {

	}

	/**
	 * Método que executa antes de salvar
	 */
	protected void beforeSave() {

	}

	/**
	 * Salva um dominio
	 */
	protected void save() throws TransactionException {
		try {
			getPersistenceService().saveOrUpdate(getDomain());
			if(getList().contains(getDomain())) {
				getList().set(getList().indexOf(getDomain()), getDomain());
			} else {
				getList().add(getDomain());
			}
		} catch (PersistenceException e) {
			throw new TransactionException(e);
		}
	}

	/**
	 * Método que executa após de salvar
	 */
	protected void afterSave() {

	}

	/**
	 * Método que executa antes de editar
	 */
	protected void beforeEdit() {

	}

	/**
	 * Edita um dominio
	 */
	protected void edit() {
		setDomain(getModel().getRowData());
	}

	/**
	 * Método que executa após de editar
	 */
	protected void afterEdit() {

	}

	/**
	 * Deleta um dominio
	 */
	protected void delete() throws TransactionException {
		try {
			getPersistenceService().delete(getRowData());
			getList().remove(getRowData());
		} catch (PersistenceException e) {
			throw new TransactionException(e);
		}
	}

	/**
	 * Método que executa antes de pesquisar
	 */
	protected void beforeSearch() {

	}

	/**
	 * Pesquisa pelo dominio
	 */
	protected void search() throws TransactionException {
		try {
			setList(getPersistenceService().findByExample(getDomain()));
		} catch (PersistenceException e) {
			throw new TransactionException(e);
		}
	}

	/**
	 * Método que executa após de pesquisar
	 */
	protected void afterSearch() {

	}

	/**
	 * Cancela uma ação
	 */
	protected void cancel() {

	}

	/**
	 * Limpa os campos da tela
	 */
	protected void clearFields() {}

	/**
	 * Valida se os atributos do dominio estão corretos
	 * 
	 * @return <b>true</b> caso se o dominio existir<br>
	 *         <b>false</b> caso o dominio não existir
	 */
	protected boolean validate() {
		return false;
	}

	/**
	 * Verifica se a dominio que será salvo já existe. Para caso seja
	 * especificado alguma propriedade como única
	 * 
	 * @return <b>true</b> caso se o dominio existir<br>
	 *         <b>false</b> caso o dominio não existir
	 */
	protected boolean existsDomain(String... properties) {
		return false;
	}

	/**
	 * Obtém o estado da tela.
	 * 
	 * @return {@link State}
	 */
	public State getState() {
		return state;
	}

	/**
	 * Atribui o estado da tela.
	 * 
	 * @param state
	 */
	protected void setState(State state) {
		this.state = state;
	}

	private boolean isDefault() {
		return getState() == State.DEFAULT;
	}

	private boolean isInsert() {
		return getState() == State.INSERT;
	}

	private boolean isEdit() {
		return getState() == State.EDIT;
	}

	private boolean isSearch() {
		return getState() == State.SEARCH;
	}

	private boolean isPreview() {
		return getState() == State.PREVIEW;
	}

	public Boolean getDisabledOnDefault() {
		return Boolean.valueOf(isDefault());
	}

	public Boolean getDisabledOnInsert() {
		return Boolean.valueOf(isInsert());
	}

	public Boolean getDisabledOnEdit() {
		return Boolean.valueOf(isEdit());
	}

	public Boolean getDisabledOnSearch() {
		return Boolean.valueOf(isSearch());
	}

	public Boolean getDisabledOnPreview() {
		return Boolean.valueOf(isPreview());
	}

	public Boolean getDisabledOnDefaultPreview() {
		return getDisabledOnDefault() || getDisabledOnPreview();
	}

	public Boolean getDisabledOnInsEdi() {
		return getDisabledOnInsert() || getDisabledOnEdit();
	}

	public Boolean getDisabledOnInsSrch() {
		return getDisabledOnInsert() || getDisabledOnSearch();
	}

	public Boolean getDisabledOnEdiSrch() {
		return getDisabledOnEdit() || getDisabledOnSearch();
	}

	public Boolean getDisabledOnInsEdiSrch() {
		return getDisabledOnInsEdi() || getDisabledOnSearch();
	}

	public Boolean getRequiredOnInsEdi() {
		return getDisabledOnInsert() || getDisabledOnEdit();
	}

	public Boolean getDisableBtnSave() {
		return !getDisabledOnInsEdi();
	}

	public Boolean getDisableBtnEdit() {
		return !getDisabledOnEdit();
	}
}